using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;

namespace Extensions.Configuration.Database.Tests
{
    public static class TestUtility
    {
        public static async Task<DatabaseConfigurationSource> GetDatabaseConfigurationSourceAsync(string json)
        {
            return new DatabaseConfigurationSource()
            {
                DatabaseOptions = await GetDbContextOptionsAsync(json),
                DatabaseProvider = new DefaultDatabaseProvider()
            };
        }

        public static async Task<DatabaseConfigurationProvider> GetConfigurationProviderAsync(string json)
        {
            var databaseOptions = await GetDbContextOptionsAsync(json);
            return new DatabaseConfigurationProvider(new DatabaseConfigurationSource()
            {
                DatabaseOptions = databaseOptions,
                DatabaseProvider = new DefaultDatabaseProvider()
            });
        }

        public static async Task<Action<DbContextOptionsBuilder>> GetDbContextOptionsAsync(string json)
        {
            var databaseName = Guid.NewGuid().ToString("N");

            // 这种写法有问题,每次执行都会重新生成一个guid,会导致获取不到数据
            // options.UseInMemoryDatabase(Guid.NewGuid().ToString("N")) 
            Action<DbContextOptionsBuilder> databaseOptions =
                options => options.UseInMemoryDatabase(databaseName);

            await WriteToDbAsync(databaseOptions, json);

            return databaseOptions;
        }

        public static async Task WriteToDbAsync(Action<DbContextOptionsBuilder> databaseOptions, string json)
        {
            var document = JsonDocument.Parse(json);
            IDictionary<string, string> data = new Dictionary<string, string>();
            ParsingJsonElement(document.RootElement, ref data);
            await using var context = new ConfigurationDatabaseContext(GetContextOptions(databaseOptions));

            if (await context.Database.EnsureCreatedAsync())
            {
            }

            await context.ConfigurationEntities.AddRangeAsync(data.Select(kv => new ConfigurationEntity()
            {
                Key = kv.Key,
                Value = kv.Value
            }));
            await context.SaveChangesAsync();
        }

        private static DbContextOptions GetContextOptions(Action<DbContextOptionsBuilder> databaseOptions)
        {
            var builder = new DbContextOptionsBuilder<ConfigurationDatabaseContext>();
            databaseOptions.Invoke(builder);
            return builder.Options;
        }

        private static void ParsingJsonElement(JsonElement root,
            ref IDictionary<string, string> result, string parentName = default)
        {
            result ??= new Dictionary<string, string>();
            switch (root.ValueKind)
            {
                case JsonValueKind.Object:
                    foreach (var item in root.EnumerateObject())
                    {
                        var name = string.IsNullOrEmpty(parentName) ? item.Name : $"{parentName}:{item.Name}";
                        ParsingJsonElement(item.Value, ref result, name);
                    }

                    break;
                case JsonValueKind.Array:
                    var index = 0;
                    foreach (var child in root.EnumerateArray())
                    {
                        ParsingJsonElement(child, ref result, $"{parentName}:{index++}");
                    }

                    break;
                case JsonValueKind.Null:
                    result.TryAdd(parentName, string.Empty);
                    break;
                case JsonValueKind.String:
                case JsonValueKind.Number:
                case JsonValueKind.True:
                case JsonValueKind.False:
                    result.TryAdd(parentName, root.ToString());
                    return;
                default: // OPTIMIZE:需要单独处理
                case JsonValueKind.Undefined:
                    return;
            }
        }
    }
}